﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using EntityTypes;
using System.Data.Objects;
using System.Data.Objects.DataClasses;
using System.Data.SqlClient;
using System.Data.EntityClient;
using System.Threading;
using System.Diagnostics;
using System.Transactions;

namespace SI2_TrabalhoFinal
{
    public partial class MarcarConsulta : Form
    {
        SI2FinalEntities entities = Connection.entities;
        private AutoResetEvent evt = new AutoResetEvent(false);
        private bool exit;

        public MarcarConsulta()
        {
            InitializeComponent();
        }

        private void MarcarConsulta_Load(object sender, EventArgs e)
        {
            var query = from p in entities.Pessoa.Include("Paciente").Where(p => p.Paciente.Count != 0)
                        select p;

            var queryMedico = from m in entities.Pessoa.Include("Medico").Where(m => m.Medico.Count != 0)
                              select m;
            var queryEspecialidade = from esp in entities.Especialidade.Include("Medico")
                                     select esp;

            var queryMotivo = from h in entities.MotivoConsulta select h;
            try
            {
                this.PacienteConsulta.DisplayMember = "Nome";
                this.PacienteConsulta.DataSource = ((ObjectQuery)query).Execute(MergeOption.AppendOnly);
                this.EspecialidadeConsulta.DisplayMember = "Nome";
                this.EspecialidadeConsulta.DataSource = ((ObjectQuery)queryEspecialidade).Execute(MergeOption.AppendOnly);
                this.MotivoConsulta.DisplayMember = "Descricao";
                this.MotivoConsulta.DataSource = ((ObjectQuery)queryMotivo).Execute(MergeOption.AppendOnly);

            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
            this.PacienteConsulta.SelectedItem = null;
            //this.EspecialidadeConsulta.SelectedItem = null;
            this.MotivoConsulta.SelectedItem = null;

        }

        private void PacienteConsulta_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (this.PacienteConsulta.SelectedItem == null)
            {
                this.NissPaciente.Text = "";
                return;
            }

            try
            {
                Pessoa p = (Pessoa)this.PacienteConsulta.SelectedItem;
                this.NissPaciente.Text = p.NumeroSS.ToString();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }


        private void EspecialidadeConsulta_SelectedIndexChanged(object sender, EventArgs e)
        {
            try
            {
                Especialidade esp = (Especialidade)this.EspecialidadeConsulta.SelectedItem;

                var x = from especialidade in entities.Especialidade
                        where especialidade.IdEspecialidade == esp.IdEspecialidade
                        from medico in especialidade.Medico
                        from pessoa in entities.Pessoa
                        where pessoa.NumeroSS == medico.IdPessoa
                        select pessoa;

                this.MedicoConsulta.DisplayMember = "Nome";
                this.MedicoConsulta.DataSource = ((ObjectQuery)x).Execute(MergeOption.OverwriteChanges);

            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void button4_Click(object sender, EventArgs e)
        {
            MarcarConsultaTentativa();
        }

        private void MarcarConsultaTentativa()
        {
            if (this.PacienteConsulta.SelectedItem == null)
            {
                MessageBox.Show("Seleccione um Paciente");
                return;
            }
            if (this.EspecialidadeConsulta.SelectedItem == null)
            {
                MessageBox.Show("Seleccione uma Especialidade");
                return;
            }
            if (this.MedicoConsulta.SelectedItem == null)
            {
                MessageBox.Show("Seleccione um Medico");
                return;
            }
            if (this.MotivoConsulta.SelectedItem == null)
            {
                MessageBox.Show("Seleccione um Motivo");
                return;
            }
            if (this.dateTimePicker1.Value <= DateTime.Now)
            {
                MessageBox.Show("Data Inválida");
                return;
            }


            ObjectParameter res = new ObjectParameter("res", typeof(int));
            try
            {
                entities.MarcarConsulta(
                    ((Pessoa)this.PacienteConsulta.SelectedItem).Paciente.First<Paciente>().NumeroBenefeciario,
                    (!this.radioButton2.Checked)?(((Pessoa)this.MedicoConsulta.SelectedItem).Medico.First<Medico>(p => p.Especialidade.ToList().Contains((Especialidade)this.EspecialidadeConsulta.SelectedItem)).LicencaMedica):
                    (((Pessoa)this.comboBoxMedicoAlternativo.SelectedItem).Medico.First<Medico>(p => p.Especialidade.ToList().Contains((Especialidade)this.EspecialidadeConsulta.SelectedItem)).LicencaMedica),
                    ((Especialidade)this.EspecialidadeConsulta.SelectedItem).IdEspecialidade,
                    this.dateTimePicker1.Value.Date,
                    ((MotivoConsulta)this.MotivoConsulta.SelectedItem).IdMotivoConsulta,
                    res);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }

            if (int.Parse(res.Value.ToString()) == -1)
                MessageBox.Show("Limites Ultrapassados. Não foi marcada a consulta");

            if (int.Parse(res.Value.ToString()) == 0)
            {
                MessageBox.Show("Consulta Marcada");
                if (this.radioButton2.Checked)
                {
                    //´Marcação de Consulta na sequência da escolha de Médico Alternativo. Temos de apagar as consultas em espera desse dia, dees pacinete nessa especialidade
                    entities.DesmarcarConsultaEmEspera(
                            ((Pessoa)this.PacienteConsulta.SelectedItem).Paciente.First<Paciente>().NumeroBenefeciario,
                            ((Especialidade)this.EspecialidadeConsulta.SelectedItem).IdEspecialidade,
                            this.dateTimePicker1.Value.Date);
                }
            }

            if (int.Parse(res.Value.ToString()) == 1)
            {
                MessageBox.Show("Limite Diário Ultrapassado. Paciente em Lista de Espera");
                this.groupBox1.Enabled = true;
                this.groupBox1.Visible = true;
                this.radioButton1.Enabled = true;
                this.radioButton1.Visible = true;
                this.radioButton2.Enabled = true;
                this.radioButton2.Visible = true;
            }
        }


        
        class MedicoList {
            private string _Nome;

            public string Nome
            {
                get { return _Nome; }
                set { _Nome = value; }
            }
            private string _Licenca;
            

            public MedicoList(string p, string p_2)
            {
                Nome = p;
                Licenca = p_2;
            }

            public string Licenca
            {
                get { return _Licenca; }
                set { _Licenca = value; }
            }
        }


        private void MarcarConsultaEmEspera(object state) {
            try
            {
                TransactionOptions to = new TransactionOptions();
                to.IsolationLevel = System.Transactions.IsolationLevel.Serializable;

                using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, to))
                {
                    using (SqlConnection conn = new SqlConnection(new SqlConnectionStringBuilder(((SqlConnection)((EntityConnection)(entities.Connection)).StoreConnection).ConnectionString)
                    {
                        AsynchronousProcessing = true
                    }.ToString()))
                    {
                        //Abrir a conexão
                        conn.Open();
                        
                        // Query para retornar os medicos e o nº de consultas que têm nesse dia
                        SqlCommand command = new SqlCommand("select * from Consulta where IdPaciente=@paciente and IdEspecialidade=@especialidade and Data=@data", conn);
                        SqlCommand comm = new SqlCommand("select p.Nome,md.LicencaMedica from Pessoa p inner join Medico md on p.NumeroSS=md.IdPessoa inner join EspecialidadeMedico em on md.LicencaMedica=em.IdMedico where em.IdEspecialidade=@especialidade and md.LicencaMedica not in (select x.LicencaMedica from (select count(*) as nr,m.LicencaMedica from Medico m inner join Consulta c on m.LicencaMedica=c.IdMedico where c.IdEspecialidade=@especialidade and c.Data=@data group by m.LicencaMedica having COUNT(m.LicencaMedica)>=(select md.NumeroPacientesDiario from Medico md where md.LicencaMedica=m.LicencaMedica)) as x)");
                        SqlCommand comm1 = new SqlCommand();
                        SqlCommand comm2 = new SqlCommand();
                    
                        //Parameters
                        SqlParameter pm1 = new SqlParameter("data", this.dateTimePicker1.Value.Date);
                        SqlParameter pm2 = new SqlParameter("especialidade", this.Invoke((Func<int>)(() => { return (((Especialidade)this.EspecialidadeConsulta.SelectedItem).IdEspecialidade); })));
                        SqlParameter pm3 = (SqlParameter)((ICloneable)pm1).Clone();
                        SqlParameter pm4 = (SqlParameter)((ICloneable)pm2).Clone();
                        SqlParameter pm5 = (SqlParameter)((ICloneable)pm1).Clone();
                        SqlParameter pm6 = (SqlParameter)((ICloneable)pm2).Clone();
                        command.Parameters.Add(new SqlParameter("paciente", this.Invoke((Func<int>)(() => { return (((Pessoa)this.PacienteConsulta.SelectedItem).Paciente.First<Paciente>().NumeroBenefeciario); }))));
                        command.Parameters.Add(new SqlParameter("especialidade", this.Invoke((Func<int>)(() => { return (((Especialidade)this.EspecialidadeConsulta.SelectedItem).IdEspecialidade); }))));
                        command.Parameters.Add(new SqlParameter("data", this.dateTimePicker1.Value.Date));
                        comm.Parameters.Add(pm1);
                        comm.Parameters.Add(pm2);
                        comm1.Parameters.Add(pm3);
                        comm1.Parameters.Add(pm4);
                        comm2.Parameters.Add(pm5);
                        comm2.Parameters.Add(pm6);
                        comm2.CommandType = CommandType.StoredProcedure;
                        comm1.CommandType = CommandType.StoredProcedure;
                        comm.Connection = conn;
                        comm1.Connection = conn;
                        comm2.Connection = conn;

                        //Adicionar ao botão o sincronizador
                        ManualResetEvent ar = new ManualResetEvent(false);
                        ((MyButton)this.button1).SetAR(ar);
                        
                        //Executar a primeira query para obter lock sobre a tabela de consulta
                        command.ExecuteNonQuery();

                        // Lidar com os controlos gráficos
                        this.Invoke((Action)(() =>
                        {
                            this.comboBoxMedicoAlternativo.Visible = true;
                            this.comboBoxMedicoAlternativo.Enabled = true;
                            this.button1.Enabled = true;
                            this.button1.Visible = true;
                            this.PacienteConsulta.Enabled = false;
                            this.MedicoConsulta.Enabled = false;
                            this.EspecialidadeConsulta.Enabled = false;
                            this.MotivoConsulta.Enabled = false;
                            this.dateTimePicker1.Enabled = false;
                            this.button4.Enabled = false;
                            this.comboBoxMedicoAlternativo.DisplayMember = "Nome";
                        }));
                    
                        //Popular a Caixa de Médico Alternativo
                        SqlDataReader dt = comm.ExecuteReader();
                        List<MedicoList> list = new List<MedicoList>();
                        while (dt.Read())
                        {
                            list.Add(new MedicoList(dt[0].ToString(), dt[1].ToString()));
                        }
                        this.Invoke((Action)(() => this.comboBoxMedicoAlternativo.DataSource = list));

                        // Esperar até ser confirmada a marcação ainda no ambito da marcação. Atenção que não estamos na thread processadora de eventos do Windows Forms
                        ar.WaitOne();

                        // Assegurar que as marcações dessa especialidade pra esse paciente nesse dia que estejam em espera são apagadas
                        SqlParameter pm10 = new SqlParameter("paciente", this.Invoke((Func<int>)(() => { return (((Pessoa)this.PacienteConsulta.SelectedItem).Paciente.First<Paciente>().NumeroBenefeciario); })));
                        comm2.Parameters.Add(pm10);
                        comm2.CommandText = "spDesmarcarConsultaEmEspera";
                        comm2.ExecuteNonQuery();
                        
                        //Fazer a marcação
                        SqlParameter pm7 = new SqlParameter("medico", this.Invoke((Func<string>)(() => { return (((MedicoList)this.comboBoxMedicoAlternativo.SelectedItem).Licenca); })));
                        SqlParameter pm8 = new SqlParameter("motivo", this.Invoke((Func<int>)(() => { return (((MotivoConsulta)this.MotivoConsulta.SelectedItem).IdMotivoConsulta); })));
                        SqlParameter pm9 = new SqlParameter("res", SqlDbType.Int);
                        pm9.Direction = ParameterDirection.Output;
                        SqlParameter pm11 = (SqlParameter)((ICloneable)pm10).Clone();
                        comm1.Parameters.Add(pm7);
                        comm1.Parameters.Add(pm8);
                        comm1.Parameters.Add(pm9);
                        comm1.Parameters.Add(pm11);
                        comm1.CommandText = "spMarcarConsulta";
                        comm1.ExecuteNonQuery();
                        
                        //Assert
                        Debug.Assert(int.Parse(pm9.Value.ToString()) == 0);

                        scope.Complete();
                        conn.Close();
                    }
                }
            }      
            catch (Exception ex3)
            {
                MessageBox.Show(ex3.Message);
            }
            finally{
                this.Invoke((Action)(() =>
                {
                    this.comboBoxMedicoAlternativo.Enabled = false;
                    this.comboBoxMedicoAlternativo.Visible = false;
                    this.button1.Enabled = false;
                    this.button1.Visible = false;
                    this.PacienteConsulta.Enabled = true;
                    this.MedicoConsulta.Enabled = true;
                    this.EspecialidadeConsulta.Enabled = true;
                    this.MotivoConsulta.Enabled = true;
                    this.dateTimePicker1.Enabled = true;
                    this.button4.Enabled = true;
                    this.groupBox1.Enabled = false;
                    this.groupBox1.Visible = false;
                    this.radioButton1.Enabled = false;
                    this.radioButton1.Visible = false;
                    this.radioButton1.Checked = false;
                    this.radioButton2.Enabled = false;
                    this.radioButton2.Visible = false;
                    this.radioButton2.Checked = false;
                }));
            }
        }

        private void radioButton1_CheckedChanged(object sender, EventArgs e)
        {
            if (this.radioButton1.Checked)
            {
                this.radioButton1.Enabled = false;
                this.radioButton2.Enabled = false;
                ThreadPool.QueueUserWorkItem(new WaitCallback(MarcarConsultaEmEspera));
            }
        }

        private void radioButton2_CheckedChanged(object sender, EventArgs e)
        {
            if (!this.radioButton2.Checked)
                return;
            this.radioButton1.Enabled = false;
            this.radioButton2.Enabled = false;
            this.comboBoxMedicoAlternativo.Visible = true;
            this.comboBoxMedicoAlternativo.Enabled = true;
            this.button1.Enabled = true;
            this.button1.Visible = true;
            this.PacienteConsulta.Enabled = false;
            this.MedicoConsulta.Enabled = false;
            this.EspecialidadeConsulta.Enabled = false;
            this.MotivoConsulta.Enabled = false;
            this.dateTimePicker1.Enabled = false;
            this.button4.Enabled = false;

            var query = from medico in
                            (
                                from c in entities.Consulta
                                where c.Data == this.dateTimePicker1.Value.Date &&
                                c.Especialidade.IdEspecialidade == ((Especialidade)this.EspecialidadeConsulta.SelectedItem).IdEspecialidade
                                group c by c.Medico into grp
                                select new
                                {
                                    Medico = grp.Key,
                                    Count = grp.Select(x => x.IdMedico).Count()
                                })
                        where medico.Count >= medico.Medico.NumeroPacientesDiario
                        from esp in entities.Especialidade where esp.IdEspecialidade==((Especialidade)this.EspecialidadeConsulta.SelectedItem).IdEspecialidade
                        from med in esp.Medico where med.LicencaMedica!=medico.Medico.LicencaMedica
                        from p in entities.Pessoa
                        where p.NumeroSS == med.IdPessoa
                        select p;
                        
            this.comboBoxMedicoAlternativo.DisplayMember = "Nome";
            this.comboBoxMedicoAlternativo.DataSource = ((ObjectQuery)query).Execute(MergeOption.OverwriteChanges);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (radioButton2.Checked) 
            {
                MarcarConsultaTentativa();
                this.comboBoxMedicoAlternativo.Enabled = false;
                this.comboBoxMedicoAlternativo.Visible = false;
                this.button1.Enabled = false;
                this.button1.Visible = false;
                this.PacienteConsulta.Enabled = true;
                this.MedicoConsulta.Enabled = true;
                this.EspecialidadeConsulta.Enabled = true;
                this.MotivoConsulta.Enabled = true;
                this.dateTimePicker1.Enabled = true;
                this.button4.Enabled = true;
                this.groupBox1.Enabled = false;
                this.groupBox1.Visible = false;
                this.radioButton1.Enabled = false;
                this.radioButton1.Visible = false;
                this.radioButton1.Checked = false;
                this.radioButton2.Enabled = false;
                this.radioButton2.Visible = false;
                this.radioButton2.Checked = false;
            }
        }
    
    
    
    
    }
}
    

